using System;
using System.Collections.Generic;
using System.IO;

namespace pal.SvnConf.Core
{
    //Understands the interpreting an svnserve configuration file with regards to user, group and acl specific operations
    public class SvnServeConfigurationFile : ISvnConfigurationProvider
    {
        private readonly string _configurationFileName;
        private static ISvnConfigurationProvider _null = new NullConfigurationProvider();
        private PasswdFile _passwdFile;
        private AuthzFile _authzFile;
        private string _authzFilePath;
        private string _passwdFilePath;

        public static ISvnConfigurationProvider NULL
        {
            get { return _null; }
        }

        public SvnServeConfigurationFile(string configurationFileName)
        {
            _configurationFileName = configurationFileName;
        }

        public void Initialize()
        {
            string contents = new StreamReader(_configurationFileName).ReadToEnd();
            List<string> lines = convertTextBlockToListOfLines(contents);
            _passwdFilePath = calculateFilePathRelativeToConfigurationFile(lines, passwdFileEntryPredicate);
            _authzFilePath = calculateFilePathRelativeToConfigurationFile(lines, authzFileEntryPredicate);
            _passwdFile = new PasswdFile(new StreamReader(_passwdFilePath));
            _authzFile = new AuthzFile(new StreamReader(_authzFilePath), _passwdFile);

            _passwdFile.Parse();
            _authzFile.Parse();
        }

        public void CreateUser(string userName, string password)
        {
            _passwdFile.AddNewUser(userName, password);
        }

        public List<User> Users
        {
            get { return _passwdFile.Users; }
        }

        public List<Group> Groups
        {
            get { return _authzFile.Groups; }
        }

        public List<Acl> Acls
        {
            get { return _authzFile.Acls; }
        }

        public List<ISecurityPrincipal> AllMembers
        {
            get
            {
                List<ISecurityPrincipal> members = new List<ISecurityPrincipal>();
                foreach (User user in _passwdFile.Users)
                    members.Add(user);
                foreach (Group group in _authzFile.Groups)
                    members.Add(group);
                return members;
            }
        }

        public void SetPassword(string userName, string newPassword)
        {
            DeleteUser(userName);
            CreateUser(userName, newPassword);
        }

        public void DeleteUser(string userName)
        {
            _passwdFile.DeleteUser(userName);
        }

        public void CreateGroup(string groupName)
        {
            _authzFile.CreateGroup(groupName);
        }

        public void DeleteGroup(string groupName)
        {
            _authzFile.DeleteGroup(groupName);
        }

        public void SaveChanges()
        {
            StreamWriter passwdFileWriter = new StreamWriter(_passwdFilePath);
            StreamWriter authzFileWriter = new StreamWriter(_authzFilePath);
            _passwdFile.Persist(passwdFileWriter);
            _authzFile.Persist(authzFileWriter);
            passwdFileWriter.Close();
            authzFileWriter.Close();
        }

        public Group FindGroupByName(string groupName)
        {
            return _authzFile.FindGroupByName(groupName);
        }

        public void CreateGroup(string groupName, List<ISecurityPrincipal> members)
        {
            _authzFile.CreateGroup(groupName, members);
        }

        private static List<string> convertTextBlockToListOfLines(string textBlock)
        {
            return new List<string>(
                textBlock.Split(new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries));
        }

        private string calculateFilePathRelativeToConfigurationFile(List<string> lines,
                                                                    Predicate<string> finderPredicate)
        {
            return Path.Combine(Path.GetDirectoryName(_configurationFileName),
                                extractFileNameFromConfiguration(lines, finderPredicate));
        }

        private string extractFileNameFromConfiguration(List<string> lines, Predicate<string> finderPredicate)
        {
            return lines.Find(finderPredicate).Split('=')[1].Trim();
        }

        private bool passwdFileEntryPredicate(string line)
        {
            return line.StartsWith("password-db");
        }

        private bool authzFileEntryPredicate(string line)
        {
            return line.StartsWith("authz-db");
        }
    }
}